package com.jdj.framework.config.autoconfigure;

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.rpc.Exporter;
import com.jdj.framework.config.autoconfigure.properties.DubboProperties;
import com.jdj.framework.core.base.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

/**
 * dubbo自动配置
 * @author caijinbang
 * @date 2019-04-14 14:20
 */
@Configuration
@ConditionalOnClass(Exporter.class)
@EnableConfigurationProperties(DubboProperties.class)
@ConditionalOnProperty(prefix = DubboProperties.PREFIX, name = "application.name")
public class DubboAutoConfiguration implements BeanFactoryPostProcessor, ApplicationContextAware, InitializingBean, EnvironmentAware {
    private static Logger logger = LoggerFactory.getLogger(DubboAutoConfiguration.class);

    private static final String COMPONENT_ID = "Duboo";

    private static ApplicationContext appCtx;

    private static Environment env;

    private static final String APPLICATION_CONFIG = "applicationConfig";

    private static final String PROTOCOL_CONFIG = "protocolConfig";

    private static final String REGISTRY_CONFIG = "registryConfig";

    private static final String PROVIDER_CONFIG = "providerConfig";

    private static final String CONSUMER_CONFIG = "consumerConfig";

    private static final String MONITOR_CONFIG = "monitorConfig";

    @Override
    public void setApplicationContext(ApplicationContext appCtx) throws BeansException {
        DubboAutoConfiguration.appCtx = appCtx;
    }

    @Override
    public void setEnvironment(Environment environment) {
        DubboAutoConfiguration.env = environment;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //实现此接口，是将当前Bean在上下文生命周期中前置
    }

    /**

     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        //不为空才加载
        String resolveProperties = getProperty(DubboProperties.PREFIX + ".resolve-properties");
        if (StringUtils.isNotEmpty(resolveProperties)) {
            Resource resource = new ClassPathResource(resolveProperties);
            System.getProperties().putAll(PropertiesLoaderUtils.loadProperties(resource));
        }
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) appCtx;
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) configurableApplicationContext.getBeanFactory();
        //ApplicationConfig
        definitionApplicationConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".application");
        //ProtocolConfig
        definitionProtocolConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".protocol");
        //RegistryConfig
        definitionRegistryConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".registry1", 1);
        definitionRegistryConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".registry2", 2);
        definitionRegistryConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".registry3", 3);
        //ProviderConfig
        definitionProviderConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".provider");
        //ConsumerConfig
        definitionConsumerConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".consumer");
        //MonitorConfig
        definitionMonitorConfig(beanDefinitionRegistry, DubboProperties.PREFIX + ".monitor");
    }

    private String getProperty(String name) {
        return DubboAutoConfiguration.env.getProperty(name);
    }

    private String getProperty(String name, String defaultValue) {
        return DubboAutoConfiguration.env.getProperty(name, defaultValue);
    }

    private void definitionApplicationConfig(BeanDefinitionRegistry beanDefinitionRegistry, String prefix) {
        if (StringUtils.isBlank(getProperty(prefix + ".name"))) {
            throw new RuntimeException(DubboProperties.PREFIX + ".application.name 不能为空");
        }
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
            ApplicationConfig.class);
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("name", getProperty(prefix + ".name", "XxxApplicationName"));
        definitionBuilder.addPropertyValue("logger", getProperty(prefix + ".logger"));
        beanDefinitionRegistry.registerBeanDefinition(APPLICATION_CONFIG, definitionBuilder.getBeanDefinition());
        LogUtils.instantiateInfo(COMPONENT_ID, APPLICATION_CONFIG, ApplicationConfig.class, logger);
    }

    private void definitionProtocolConfig(BeanDefinitionRegistry beanDefinitionRegistry, String prefix) {
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
            ProtocolConfig.class);
        definitionBuilder.addPropertyValue("id", "dubboInnerService");
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("name", getProperty(prefix + ".name", "dubbo"));
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("port", getProperty(prefix + ".port", "20880"));
        definitionBuilder.addPropertyValue("payload", getProperty(prefix + ".payload"));
        beanDefinitionRegistry.registerBeanDefinition(PROTOCOL_CONFIG, definitionBuilder.getBeanDefinition());
        LogUtils.instantiateInfo(COMPONENT_ID, PROTOCOL_CONFIG, ProtocolConfig.class, logger);
    }

    private void definitionRegistryConfig(BeanDefinitionRegistry beanDefinitionRegistry, String prefix, int num) {
        if (StringUtils.isNotEmpty(getProperty(prefix + ".address"))) {
            BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
                RegistryConfig.class);
            //如果配置
            if (StringUtils.isNotEmpty(getProperty(prefix + ".id"))) {
                definitionBuilder.addPropertyValue("id", getProperty(prefix + ".id"));
            } else {
                //否则提供默认
                definitionBuilder.addPropertyValue("id", REGISTRY_CONFIG + num);
            }
            //按spring-configuration-metadata.json备注的，提供默认值
            definitionBuilder.addPropertyValue("protocol", getProperty(prefix + ".protocol", "zookeeper"));
            definitionBuilder.addPropertyValue("address", getProperty(prefix + ".address"));
            if (getProperty(prefix + ".register") != null) {
                definitionBuilder.addPropertyValue("register", getProperty(prefix + ".register"));
            }
            if (getProperty(prefix + ".subscribe") != null) {
                definitionBuilder.addPropertyValue("subscribe", getProperty(prefix + ".subscribe"));
            }
            if (getProperty(prefix + ".default") != null) {
                definitionBuilder.addPropertyValue("default", getProperty(prefix + ".default"));
            }
            //如果配置
            if (StringUtils.isNotEmpty(getProperty(prefix + ".id"))) {
                beanDefinitionRegistry.registerBeanDefinition(getProperty(prefix + ".id"), definitionBuilder.getBeanDefinition());
            } else {
                //否则提供默认
                beanDefinitionRegistry.registerBeanDefinition(REGISTRY_CONFIG + num, definitionBuilder.getBeanDefinition());
            }
            //使用文件缓存注册中心地址列表及服务提供者列表，应用重启时将基于此文件恢复，注意：两个注册中心不能使用同一文件存储
            definitionBuilder.addPropertyValue("file", getProperty(prefix + ".file"));
            LogUtils.instantiateInfo(COMPONENT_ID, REGISTRY_CONFIG + num, RegistryConfig.class, logger);
        }
    }

    private void definitionProviderConfig(BeanDefinitionRegistry beanDefinitionRegistry, String prefix) {
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
            ProviderConfig.class);
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("filter", getProperty(prefix + ".filter", "dubboProviderFilter"));
        if (getProperty(prefix + ".timeout") != null) {
            definitionBuilder.addPropertyValue("timeout", getProperty(prefix + ".timeout"));
        }
        if (getProperty(prefix + ".retries") != null) {
            definitionBuilder.addPropertyValue("retries", getProperty(prefix + ".retries"));
        }
        if (getProperty(prefix + ".delay") != null) {
            definitionBuilder.addPropertyValue("delay", getProperty(prefix + ".delay"));
        }
        beanDefinitionRegistry.registerBeanDefinition(PROVIDER_CONFIG, definitionBuilder.getBeanDefinition());
        LogUtils.instantiateInfo(COMPONENT_ID, PROVIDER_CONFIG, ProviderConfig.class, logger);
    }

    private void definitionConsumerConfig(BeanDefinitionRegistry beanDefinitionRegistry, String prefix) {
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
            ConsumerConfig.class);
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("filter", getProperty(prefix + ".filter", "dubboConsumerFilter"));
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("timeout", getProperty(prefix + ".timeout", "1000000"));
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("check", getProperty(prefix + ".check", "false"));
        //按spring-configuration-metadata.json备注的，提供默认值
        definitionBuilder.addPropertyValue("retries", getProperty(prefix + ".retries", "0"));
        beanDefinitionRegistry.registerBeanDefinition(CONSUMER_CONFIG, definitionBuilder.getBeanDefinition());
        LogUtils.instantiateInfo(COMPONENT_ID, CONSUMER_CONFIG, ConsumerConfig.class, logger);
    }

    private void definitionMonitorConfig(BeanDefinitionRegistry beanDefinitionRegistry, String prefix) {
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
            MonitorConfig.class);
        //监控中心协议，如果为protocol="registry"，表示从注册中心发现监控中心地址，否则直连监控中心
        definitionBuilder.addPropertyValue("protocol", getProperty(prefix + ".protocol"));
        beanDefinitionRegistry.registerBeanDefinition(MONITOR_CONFIG, definitionBuilder.getBeanDefinition());
        LogUtils.instantiateInfo(COMPONENT_ID, MONITOR_CONFIG, MonitorConfig.class, logger);
    }
}
